
#ifndef __BMANLIB__TILE_H__
#define __BMANLIB__TILE_H__

#include "coid/comm/dynarray.h"
#include "coid/comm/rnd.h"

#include "powerup.h"
#include "bomb.h"
#include "moving.h"

using namespace coid;



////////////////////////////////////////////////////////////////////////////////
struct TILE
{
    uchar type;             enum { EMPTY=0, SOFT=1, HARD=2, SOFTBURNING=3 };
    uchar ground;           enum { GRASS=0, HOLE=8, FIRE, SPRING, ESCALATOR, NORTHARROW, SOUTHARROW, WESTARROW, EASTARROW };

    ///ground flags
    enum {
        xGROUND_TYPE            = 0x0f,
        fNOSTART                = 0x80,     ///< player cannot start here
    };

    int8 vdir;
    int8 hdir;
    
    enum {
        rBOMBID                 = 4,
        xMANID                  = 0x0f,
    };
    uchar vowner;               ///< vertical flame ignitor/bomb
    uchar howner;               ///< horizontal flame ignitor/bomb
    uchar vcount;               ///< vertical flame overlay count
    uchar hcount;               ///< horizontal flame overlay count

    dynarray<POWERUP> _pow;
    rsp_dynarray<moving,false> _obj;

    static collisionbody _tilebody;



    uchar get_vman() const      { return vowner & xMANID; }
    uchar get_hman() const      { return howner & xMANID; }
    uchar get_vbomb() const     { return vowner >> rBOMBID; }
    uchar get_hbomb() const     { return howner >> rBOMBID; }

    void set_type( uchar t, uchar g )
    {
        type = t;
        ground = g;
    }

    friend binstream& operator << ( binstream& bin, const TILE& t )
    {
        bin.xwrite_raw( &t.type, 8 );
        DASSERT( t._pow.size() < 256 );
        uchar n = (uchar)t._pow.size();
        bin << n;
        if(n)
            bin.xwrite_raw( t._pow.ptr(), t._pow.byte_size() );
        //t._obj.stream_out(bin);
        return bin;
    }

    friend binstream& operator >> ( binstream& bin, TILE& t )
    {
        bin.xread_raw( &t.type, 8 );
        uchar n;
        bin >> n;
        t._pow.need_new(n);
        if(n)
            bin.xread_raw( t._pow.ptr(), t._pow.byte_size() );
        //t._obj.stream_in(bin);
        return bin;
    }

    void add_object( moving* obj )      { *_obj.add(1) = obj; }
    bool del_object( moving* obj )      { return _obj.del_key(obj) != UMAX; }

    moving* get_tied_object( const moving* m ) const
    {
        for( uint i=0; i<_obj.size(); ++i )
        {
            if( _obj[i] == m )  continue;
            if( !_obj[i]->on_ground() )  continue;

            if( _obj[i]->get_man() == m->get_man() )
                return _obj[i];
        }
        return 0;
    }

    bool is_burning() const             { return vcount+hcount != 0; }
    int fire_owner() const              { return vcount ? vowner : howner; }

    bool has_bomb() const
    {
        for( uint i=0; i<_obj.size(); ++i )
            if( !_obj[i]->is_man() && _obj[i]->_xyzd0.z <= coord(0) )  return true;
        return false;
    }

    bool is_obstacle() const
    {
        if( type != EMPTY )  return true;
        for( uint i=0; i<_obj.size(); ++i )
            if( !_obj[i]->is_man() && _obj[i]->is_stopped() )  return true;
        return false;
    }

    bool is_obstacle_tile() const
    {
        return type != EMPTY;
    }

    bool has_object() const
    {
        return _obj.size() > 0;
    }

    int burn_bomb( uchar plybomb )
    {
        for( uint i=0; i<_obj.size(); ++i )
        {
            if( !_obj[i]->is_man() && _obj[i]->on_ground() )
            {
                ((bomb*)_obj[i])->prepare_detonation( plybomb, 0 );
                return 0;
            }
        }
        return 1;
    }

    /// return -1 if stop and no clean, 0 if stop and clean, 1 if ok
    int set_flamev( uchar plybomb, int8 d, uchar ignitor )
    {
        if( type == HARD )
            return -1;

        if( type == SOFT ) {
            type = SOFTBURNING;
            return 0;
        }

        if( type == SOFTBURNING )
            return -1;
        
        if( _pow.size() > 0 ) {
            _pow.reset();           //this is when powerups really burn
            return -1;
        }

        vdir += d;
        vowner = plybomb;
        ++vcount;

        return burn_bomb(ignitor);
    }

    int set_flameh( uchar plybomb, int8 d, uchar ignitor )
    {
        //if( howner != 0xff )  return false;
        if( type == HARD )
            return -1;

        if( type == SOFT ) {
            type = SOFTBURNING;
            return 0;
        }

        if( type == SOFTBURNING )
            return -1;

        if( _pow.size() > 0 ) {
            _pow.reset();           //this is when powerups really burn
            return -1;
        }

        hdir += d;
        howner = plybomb;
        ++hcount;

        return burn_bomb(ignitor);
    }

    void set_flamehv( uchar plybomb )
    {
        vowner = howner = plybomb;
        ++vcount;
        ++hcount;
    }

    bool clr_flamev( int8 d )
    {
        //if( type == HARD )  return false;
        if( type == SOFTBURNING )
        {
            type = EMPTY;
            return false;
        }
        vdir -= d;
        --vcount;
        return true;
    }

    bool clr_flameh( int8 d )
    {
        //if( type == HARD )  return false;
        if( type == SOFTBURNING )
        {
            type = EMPTY;
            return false;
        }
        hdir -= d;
        --hcount;
        return true;
    }

    void clr_flamehv()
    {
        --vcount;
        --hcount;
    }

    void add_powerup( POWERUP p )       { _pow.push(p); }

    TILE()
    {
        type = EMPTY;
        ground = GRASS;
        vcount = hcount = 0;
        vdir = hdir = 0;
        vowner = howner = 0xff;
    }
};

#endif //__BMANLIB__TILE_H__
